package TangramBase;

import TangramCore.Database;
import TangramCore.Dilek;
import TangramCore.Hladina;
import TangramCore.HladinaObrys;
import TangramCore.Obrazec;
import TangramCore.Obrys;
import TangramCore.Poloha;
import TangramCore.Skladacka;
import TangramCore.Vzhled;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
//import de.schlichtherle.io.swing.JFileTree;
import java.util.ArrayList;
import java.util.Vector;

import javax.swing.*;

import Pentominos.PentominoSolutionListener;
import java.applet.Applet;
import java.net.URL;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.filechooser.FileFilter;
import java.text.Collator;
import java.util.ListIterator;

public class TangramFrame extends JFrame implements PentominoSolutionListener,SolutionListener,AdjustmentListener,MouseListener,MouseWheelListener,ActionListener,Database.DatabaseChangeListener,DocumentWindow,WindowStateListener{
	static final long serialVersionUID = 24362462L;
	
	private JMenuItem mniThis = null;
    private int iWindow = -1, lastState;

    private static final String sTooltipFormatString = "<html><style>p {width: 200px; margin: 0px; font-size: 100%; font-weight: bold;} h1 {margin: 0px;} h2 {text-decoration: underline; margin: 0px; font-size: 90%;}</style><h2>Name</h2><h1>%name</h1><h2>Description</h2><p>%description</p><h2>Author</h2><p>%author</p><h2>Date</h2><p>%date</p><h2>Keywords</h2><p>%keywords</p>";
    private static final String sApp = "TangramBase", sFigures = " figures";
    private static Collator collator = Collator.getInstance();
    
    static final int ID_OPEN=1, ID_SAVE=2, ID_SAVEAS=3, ID_KONEC=4, ID_COPY=5, ID_PAST=6, ID_INVERT=7, ID_SELALL=8,
            ID_SELNONE=9, ID_FILTER=10, ID_SOLVE=11, ID_EDIT=12, ID_SORTBYNAME=13, ID_ZRUSFILTER=14, ID_MASSDESCRIBE=15,
            ID_DELETE=16,ID_NEWFIGURE=17,ID_RUNSOLVER=18,ID_SHOWSOLUTIONS=19,ID_DESCRIBE=20,ID_REPAIR=21,
            ID_CLOSEALL=22,ID_MAXIMIZEALL=23,ID_TILE=24,ID_CASCADE=25,ID_RUNPENTOMINOS=26,
            ID_SHOWTOOLTIPS=27,ID_SHOWLABELS=28,ID_SHOWNAME=29,ID_SHOWDATE=30,ID_SHOWAUTHOR=31,
            ID_SORTBYDATE=32,ID_SORTBYAUTHOR=33,ID_SORTREVERSE=34,ID_TOGGLECOLORS=35,ID_HELPCONTENTS=36,ID_HELPABOUT=37,
            ID_LAYOUTEDITOR=38,ID_SKLADACKA0=1024, ID_WINDOW0=2048;
    
    static final int SCROLL_KEEP=-1, SCROLL_FIRST=0, SCROLL_LAST=1;
    static int ID_SKLADACKA[];
    public static int iVzhled = 0;
    public static Vzhled vObrys[] = {Vzhled.getDefaultNahledFilled(), Vzhled.getDefaultObrys()}, vDilky[] = {PentominosConversion.getColoredVzhled(), Vzhled.getDefaultNahledDilek()};
    
    JMenuBar menuBar;
    JMenu mnuSoubor, mnuSet, mnuNewDB, mnuSelection, mnuOrganize, mnuWindows, mnuHelp;
    JCheckBoxMenuItem mniShowSolutions, mniShowLabels, mniShowTooltips,mniSortReverse;
    JRadioButtonMenuItem mniShowName, mniShowDate, mniShowAuthor;
    ButtonGroup groupLabel;
    JMenuItem mniNewFigure, mniSolve, mniEdit, mniDescribe, mniSolver, mniPentominos, mniSkladacka[], mniOpen, mniSave, mniSaveAs,mniRepair,
            mniKonec, mniCopy, mniPast, mniMassDescribe, mniDelete, mniInvert, mniSelAll, mniSelNone, mniSortByName, mniSortByDate, mniSortByAuthor,
            mniFilter, mniZrusFilter, mniCloseAll, mniMaximizeAll, mniTile, mniCascade, mniToggleColors,mniHelpContents,mniHelpAbout,mniLayoutEditor;

    Database database;
    DefaultTreeModel treeModel;
    Obrazec obr[];
    JPanel panCenter = new JPanel(new GridLayout(hCount,wCount));
    JPanel panEast = new JPanel(new BorderLayout(0,0));
    JPanel panObr[];
    JLabel lblObr[];
    JScrollBar scrollNahledy = new JScrollBar(Adjustable.VERTICAL,0,hCount,0,hCount);
    
    String FILE_NEWDB = "data/new", FILE_SETTINGS = "settings.ini", DIR_SKLADACKY = "sets/", FILE_COMMONKWD = "sets/common.kwd", rootDir = null;
    HladinaObrys ho [];
    Dilek d[];
    Obrys o;
    
    int pozice = 0,   sel = -1, showedFilter = -1;
    Database.ObrazecData odShowedDescr, odShowedFilter;
    
    static final int wCount = 8, hCount = 6, Count = wCount * hCount;
    int lastPos = 0;
    boolean obrInit = false, quittingSolver = false, quittingPentominoSolver = true;
    
    ArrayList<DocumentWindow> lstWindows = new ArrayList<DocumentWindow>(20);
    /*TangramEditFrame frmEdit;
    FilterFrame frmFilter;
    MassDescriptionFrame frmMass;*/
    ProgressFrame frmProgress;
    PentominoSolverFrame frmPentominos;
    TangramSolverFrame frmTangramSolver;
    
    JFileChooser chooserOpen = new JFileChooser(), chooserSave = new JFileChooser();
    boolean pentomino;
    
    /*private static class DatabaseFilter extends javax.swing.filechooser.FileFilter{
        private String ext, des;
        public DatabaseFilter(String e, String d){
            ext = "." + e;
            des = d + " (*" + ext +")";
        }
        @Override
        public boolean accept(File f){
            return f.getName().endsWith(ext);
        }
        @Override
        public String getDescription(){
            return des;
        }
    }*/
    
    public static class SortedTreeNode extends DefaultMutableTreeNode {
    	static final long serialVersionUID = 24362462L;
    	
    	private static Comparator<Object> nodeComparator = new Comparator<Object>(){
            @Override
            public int compare(Object o1, Object o2) {
                return collator.compare(o1.toString(),o2.toString());
            }
            @Override
            public boolean equals(Object obj) {
                return false;
            }
            @Override
            public int hashCode() {
                int hash = 5;
                return hash;
            }
        };
        public SortedTreeNode(Object userData){
            super(userData);
        }
        @Override
        public void add(MutableTreeNode node){
            super.add(node);
            //Collections.<Object>sort(this.children, nodeComparator);
        }
        
        @SuppressWarnings("unchecked")
        public void sort(){
            if(children == null || children.isEmpty())
                return;
            Collections.sort(children, nodeComparator);
            for(Object child: children)
                if(child instanceof SortedTreeNode)
                    ((SortedTreeNode)child).sort();
        }
    }
    
    public TangramFrame() {
        collator.setStrength(Collator.TERTIARY);
        //File f = new File(".");
        //String sa = f.getAbsolutePath();
        //try{
        //rootDir = getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
        //rootDir = new File(".").getAbsolutePath();
        rootDir = System.getProperty("user.dir") + "/";
        //rootDir = rootDir.substring(0, rootDir.endsWith("/classes/") ? rootDir.lastIndexOf("classes/") : rootDir.lastIndexOf('/')+1);
        /*}catch(URISyntaxException ex){
            System.out.println(ex.toString());
        }*/
//sa = sa.substring(0,sa.length()-1);
        File fndb = new File(rootDir + FILE_NEWDB);
        chooserSave.setSelectedFile(fndb);
        chooserOpen.setSelectedFile(fndb);
        //FILE_IMAGE = sa + FILE_IMAGE;
        //FILE_NEWDB = rootDir + FILE_NEWDB;
        //FILE_SETTINGS = rootDir + FILE_SETTINGS;
        //DIR_SKLADACKY = rootDir + DIR_SKLADACKY;
        //FILE_COMMONKWD = rootDir + FILE_COMMONKWD;
        getContentPane().addMouseWheelListener(this);
        scrollNahledy.addAdjustmentListener(this);
        scrollNahledy.addMouseWheelListener(this);
        obr = new Obrazec[Count];
        
        panEast.add(scrollNahledy,BorderLayout.CENTER);
        getContentPane().add(panEast,BorderLayout.EAST);
        
        int modifier = InputEvent.CTRL_MASK;
        try {
            String OS = System.getProperty("os.name");
            if (OS.startsWith("Mac"))
                modifier = InputEvent.META_MASK;
        } catch (Exception e) {
        }
        menuBar = new JMenuBar();
        mnuSoubor = new JMenu("Database");
        mnuSet = new JMenu("Figure");
        mnuSet.setEnabled(false);
        mniNewFigure = new JMenuItem("New...");
        mniNewFigure.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, modifier));
        mniSolve = new JMenuItem("Solve...");
        mniEdit = new JMenuItem("Edit...");
        mniDescribe = new JMenuItem("Describe...");
        mniSolver = new JMenuItem("Run Universal Solver...");
        //mniSolver.setEnabled(false);
        mniPentominos = new JMenuItem("Run Pentomino Solver...");
        mniPentominos.setVisible(false);
        mnuNewDB = new JMenu("New...");
        mniOpen = new JMenuItem("Open...");
        mniOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,modifier));
        mniSave = new JMenuItem("Save");
        mniSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,modifier));
        mniSaveAs = new JMenuItem("Save as...");
        mniShowSolutions = new JCheckBoxMenuItem("Show solutions");
        mniToggleColors = new JMenuItem("Toggle colors");
        //mniShowSolutions.setEnabled(false);
        mniRepair = new JMenuItem("Repair");
        mniRepair.setEnabled(false);
        mniLayoutEditor = new JMenuItem("Layout Editor...");
        //mniRepair.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,modifier));
        mniKonec = new JMenuItem("Quit");
        mniKonec.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,modifier));
        mnuSelection = new JMenu("Selection");
        mnuSelection.setEnabled(false);
        mniCopy = new JMenuItem("Copy");
        mniCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,modifier));
        mniPast = new JMenuItem("Past");
        mniPast.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,modifier));
        mniMassDescribe = new JMenuItem("Mass describe...");
        mniMassDescribe.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_K,modifier));
        mniDelete = new JMenuItem("Delete");
        mniDelete.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0));
        mniInvert = new JMenuItem("Invert");
        mniInvert.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MULTIPLY,0));
        mniSelAll = new JMenuItem("Select all");
        mniSelAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,modifier));
        mniSelNone = new JMenuItem("Select none");
        mnuOrganize = new JMenu("Organize");
        mnuOrganize.setEnabled(false);
        mniShowLabels = new JCheckBoxMenuItem("Show labels",true);
        mniShowTooltips = new JCheckBoxMenuItem("Show tooltips");
        groupLabel = new ButtonGroup();
        mniShowName = new JRadioButtonMenuItem("Show name",true);
        mniShowDate = new JRadioButtonMenuItem("Show date");
        mniShowAuthor = new JRadioButtonMenuItem("Show author");
        groupLabel.add(mniShowName);
        groupLabel.add(mniShowDate);
        groupLabel.add(mniShowAuthor);
        mniSortReverse = new JCheckBoxMenuItem("Reverse order");
        mniSortByName = new JMenuItem("Sort by name");
        mniSortByDate = new JMenuItem("Sort by date");
        mniSortByAuthor = new JMenuItem("Sort by author");
        mniFilter = new JMenuItem("Filter...");
        mniFilter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F,modifier));
        mniZrusFilter = new JMenuItem("Remove filter");
        mnuHelp = new JMenu("Help");
        mniHelpContents = new JMenuItem("Contents");
        mniHelpAbout = new JMenuItem("About");
        
        mnuSoubor.add(mnuNewDB);
        mnuSoubor.add(mniOpen);
        mniOpen.setActionCommand(String.valueOf(ID_OPEN));
        mniOpen.addActionListener(this);
        mnuSoubor.add(mniSave);
        mniSave.setActionCommand(String.valueOf(ID_SAVE));
        mniSave.addActionListener(this);
        mnuSoubor.add(mniSaveAs);
        mniSaveAs.setActionCommand(String.valueOf(ID_SAVEAS));
        mniSaveAs.addActionListener(this);
        mnuSoubor.add(mniRepair);
        mniRepair.setActionCommand(String.valueOf(ID_REPAIR));
        mniRepair.addActionListener(this);
        mnuSoubor.addSeparator();
        mnuSoubor.add(mniShowSolutions);
        mniShowSolutions.setActionCommand(String.valueOf(ID_SHOWSOLUTIONS));
        mniShowSolutions.addActionListener(this);
        mnuSoubor.add(mniToggleColors);
        mniToggleColors.setActionCommand(String.valueOf(ID_TOGGLECOLORS));
        mniToggleColors.addActionListener(this);
        mniToggleColors.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B,0));
        //mniSolver.setEnabled(false);
        mnuSoubor.addSeparator();
        mnuSoubor.add(mniPentominos);
        mniPentominos.setActionCommand(String.valueOf(ID_RUNPENTOMINOS));
        mniPentominos.addActionListener(this);
        mnuSoubor.add(mniSolver);
        mniSolver.setActionCommand(String.valueOf(ID_RUNSOLVER));
        mniSolver.addActionListener(this);
        mnuSoubor.addSeparator();
        mnuSoubor.add(mniLayoutEditor);
        mniLayoutEditor.setActionCommand(String.valueOf(ID_LAYOUTEDITOR));
        mniLayoutEditor.addActionListener(this);
        mnuSoubor.addSeparator();
        mnuSoubor.add(mniKonec);
        mniKonec.setActionCommand(String.valueOf(ID_KONEC));
        mniKonec.addActionListener(this);
        menuBar.add(mnuSoubor);
        mnuSet.add(mniNewFigure);
        mniNewFigure.setActionCommand(String.valueOf(ID_NEWFIGURE));
        mniNewFigure.addActionListener(this);
        mnuSet.addSeparator();
        mnuSet.add(mniSolve);
        mniSolve.setActionCommand(String.valueOf(ID_SOLVE));
        mniSolve.addActionListener(this);
        mnuSet.add(mniEdit);
        mniEdit.setActionCommand(String.valueOf(ID_EDIT));
        mniEdit.addActionListener(this);
        mnuSet.addSeparator();
        mnuSet.add(mniDescribe);
        mniDescribe.setActionCommand(String.valueOf(ID_DESCRIBE));
        mniDescribe.addActionListener(this);
        menuBar.add(mnuSet);
        mnuSelection.add(mniCopy);
        mniCopy.setActionCommand(String.valueOf(ID_COPY));
        mniCopy.addActionListener(this);
        mnuSelection.add(mniPast);
        mniPast.setActionCommand(String.valueOf(ID_PAST));
        mniPast.addActionListener(this);
        mnuSelection.addSeparator();
        mnuSelection.add(mniMassDescribe);
        mniMassDescribe.setActionCommand(String.valueOf(ID_MASSDESCRIBE));
        mniMassDescribe.addActionListener(this);
        mnuSelection.add(mniDelete);
        mniDelete.setActionCommand(String.valueOf(ID_DELETE));
        mniDelete.addActionListener(this);
        mnuSelection.addSeparator();
        mnuSelection.add(mniInvert);
        mniInvert.setActionCommand(String.valueOf(ID_INVERT));
        mniInvert.addActionListener(this);
        mnuSelection.add(mniSelAll);
        mniSelAll.setActionCommand(String.valueOf(ID_SELALL));
        mniSelAll.addActionListener(this);
        mnuSelection.add(mniSelNone);
        mniSelNone.setActionCommand(String.valueOf(ID_SELNONE));
        mniSelNone.addActionListener(this);
        menuBar.add(mnuSelection);
        mnuOrganize.add(mniSortByName);
        mniSortByName.setActionCommand(String.valueOf(ID_SORTBYNAME));
        mniSortByName.addActionListener(this);
        mnuOrganize.add(mniSortByDate);
        mniSortByDate.setActionCommand(String.valueOf(ID_SORTBYDATE));
        mniSortByDate.addActionListener(this);
        mnuOrganize.add(mniSortByAuthor);
        mniSortByAuthor.setActionCommand(String.valueOf(ID_SORTBYAUTHOR));
        mniSortByAuthor.addActionListener(this);
        mnuOrganize.add(mniSortReverse);
        //mniSortReverse.setActionCommand(String.valueOf(ID_SORTREVERSE));
        //mniSortReverse.addActionListener(this);
        mnuOrganize.addSeparator();
        mnuOrganize.add(mniFilter);
        mniFilter.setActionCommand(String.valueOf(ID_FILTER));
        mniFilter.addActionListener(this);
        mnuOrganize.add(mniZrusFilter);
        mniZrusFilter.setActionCommand(String.valueOf(ID_ZRUSFILTER));
        mniZrusFilter.addActionListener(this);
        mnuOrganize.addSeparator();
        mnuOrganize.add(mniShowTooltips);
        mniShowTooltips.setActionCommand(String.valueOf(ID_SHOWTOOLTIPS));
        mniShowTooltips.addActionListener(this);
        mnuOrganize.add(mniShowLabels);
        mniShowLabels.setActionCommand(String.valueOf(ID_SHOWLABELS));
        mniShowLabels.addActionListener(this);
        mnuOrganize.addSeparator();
        mnuOrganize.add(mniShowName);
        mniShowName.setActionCommand(String.valueOf(ID_SHOWNAME));
        mniShowName.addActionListener(this);
        mnuOrganize.add(mniShowDate);
        mniShowDate.setActionCommand(String.valueOf(ID_SHOWDATE));
        mniShowDate.addActionListener(this);
        mnuOrganize.add(mniShowAuthor);
        mniShowAuthor.setActionCommand(String.valueOf(ID_SHOWAUTHOR));
        mniShowAuthor.addActionListener(this);
        menuBar.add(mnuOrganize);
        mnuWindows = new JMenu("Windows");
        mniCloseAll = new JMenuItem("Close All");
        mniCloseAll.setActionCommand(String.valueOf(ID_CLOSEALL));
        mniCloseAll.addActionListener(this);
        //mniMaximizeAll = new JMenuItem("Maximize All");
        //mniMaximizeAll.setActionCommand(String.valueOf(ID_MAXIMIZEALL));
        //mniMaximizeAll.addActionListener(this);
        mniCascade = new JMenuItem("Cascade");
        mniCascade.setActionCommand(String.valueOf(ID_CASCADE));
        mniCascade.addActionListener(this);
        mniTile = new JMenuItem("Tile");
        mniTile.setActionCommand(String.valueOf(ID_TILE));
        mniTile.addActionListener(this);
        updateWindowsMenu();
        menuBar.add(mnuWindows);
        mnuHelp.add(mniHelpContents);
        mniHelpContents.setEnabled(false);
        mniHelpContents.setActionCommand(String.valueOf(ID_HELPCONTENTS));
        mniHelpContents.addActionListener(this);
        mnuHelp.addSeparator();
        mnuHelp.add(mniHelpAbout);
        mniHelpAbout.setActionCommand(String.valueOf(ID_HELPABOUT));
        mniHelpAbout.addActionListener(this);
        menuBar.add(mnuHelp);
        setJMenuBar(menuBar);
        //pack();
        
        database = new Database(DIR_SKLADACKY, null);
        database.addDatabaseChangeListener(this);
        setTitle();
        initSkladacky(database);
        
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e){
                exit();
            }
            public void windowOpened(WindowEvent e){
                loadSettings();
            }
            public void windowActivated(WindowEvent e){
                scrollNahledy.requestFocus();
            }
        });
        addWindowStateListener(this);
        frmProgress = new ProgressFrame(this);
    }
    
    private static SortedTreeNode loadTree(String f){
        BufferedReader in = null;
        SortedTreeNode root = new SortedTreeNode("Keywords");
        try{
            in = new BufferedReader(new InputStreamReader(new FileInputStream(f),"UTF8"));
            String s;
            SortedTreeNode parent = root, child = root;
            int lastLevel = 0;
            while(!((s = in.readLine()) == null || s.equals(""))){
                int actLevel = getLevel(s);
                s = s.substring(actLevel);
                if(actLevel==lastLevel)
                    parent.add(child = new SortedTreeNode(s));
                else if(actLevel>lastLevel){
                    parent = child;
                    child.add(child = new SortedTreeNode(s));
                    lastLevel = actLevel;
                } else{
                    while(lastLevel>actLevel){
                        lastLevel--;
                        parent = (SortedTreeNode)parent.getParent();
                    }
                    parent.add(child = new SortedTreeNode(s));
                }
            }
            in.close();
        } catch(IOException ex){
            System.out.println("Keyword file " + f + " not found.");
        }
        root.sort();
        return root;
    }
    
    @SuppressWarnings("unchecked")
	public SortedTreeNode joinTrees(SortedTreeNode nodeTo, SortedTreeNode nodeFrom){
        HashMap<String, Integer> nodesTo = new HashMap<String, Integer>(nodeTo.getChildCount());
        Enumeration<SortedTreeNode> childsTo = nodeTo.children();
        while(childsTo.hasMoreElements()){
            SortedTreeNode childTo = (SortedTreeNode)childsTo.nextElement();
            nodesTo.put((String)childTo.getUserObject(),nodeTo.getIndex(childTo));
        }
        Enumeration<SortedTreeNode> childsFrom = nodeFrom.children();
        List<SortedTreeNode> listAdd = new LinkedList<SortedTreeNode>();
        while(childsFrom.hasMoreElements()){
            SortedTreeNode childFrom = (SortedTreeNode)childsFrom.nextElement();
            Integer same = nodesTo.get((String)childFrom.getUserObject());
            if(same == null)
                listAdd.add(childFrom);
            else
                joinTrees((SortedTreeNode)nodeTo.getChildAt(same),childFrom);
        }
        for(SortedTreeNode nodeAdd: listAdd){
            nodeTo.add(nodeAdd);
        }
        return nodeTo;
    }
    
    private static int getLevel(String s){
        int ret = 0;
        while(s.charAt(ret)=='\t')
            ret++;
        return ret;
    }
    
    public void loadKeywords(String f){
        SortedTreeNode root = joinTrees(loadTree(f), loadTree(FILE_COMMONKWD));
        root.sort();
        treeModel = new DefaultTreeModel(root);
    }
    
    private int initSkladacky(Database d){
        String sNazev[] = d.getSetsSorted();
        mniSkladacka = new JMenuItem[sNazev.length];
        ID_SKLADACKA = new int[sNazev.length];
        FileFilter fDefault = null;
        for(int i=0; i<sNazev.length; i++){
            mniSkladacka[i] = new JMenuItem(sNazev[i]);
            ID_SKLADACKA[i] = ID_SKLADACKA0 + i;
            mniSkladacka[i].setActionCommand(String.valueOf(ID_SKLADACKA[i]));
            mniSkladacka[i].addActionListener(this);
            mnuNewDB.add(mniSkladacka[i]);
            Skladacka skl = d.getSet(sNazev[i]);
            FileFilter f = skl.getFileFilter();
            if(skl.isTangram())
                fDefault = f;
            chooserOpen.addChoosableFileFilter(f);
        }
        if(fDefault != null)
            chooserOpen.setFileFilter(fDefault);
        return sNazev.length;
    }
    public void loadSettings(){
        try{
            BufferedReader in = new BufferedReader(new FileReader(new File(FILE_SETTINGS)));
            String s = in.readLine();
            in.close();
            if(s != null)
                openDatabase(new File(s));
        }catch(IOException ex){}
    }
    public boolean askSaveCancel(){
        if(database.hasChanged()){
            int ret;
            ret = JOptionPane.showConfirmDialog(this, "Do you want to save the database?", "Changes not saved", JOptionPane.YES_NO_CANCEL_OPTION);
            if (ret == JOptionPane.CANCEL_OPTION ||
                    ret == JOptionPane.CLOSED_OPTION)
                return true;
            else if (ret == JOptionPane.OK_OPTION)
                if(!save())
                    saveAs();
        }
        return false;
    }
    public void exit(){
        if(askSaveCancel())
            return;
        String f =  database.getFile();
        if(f != null){
            try {
                BufferedWriter out = new BufferedWriter(new FileWriter(new File(FILE_SETTINGS)));
                out.write(f);
                out.close();
            } catch (IOException ex) {
                System.out.println("Could not save settings.");
            }
        }
        System.exit(0);
    }
    private void initObrazce(){
        ho = new HladinaObrys[Count];
        Skladacka skl = database.getActSet();
        if(skl==null)
            return;
        d = skl.getDilky();
        o = new Obrys(d);
        panObr = new JPanel[Count];
        lblObr = new JLabel[Count];
        boolean sel = mniShowSolutions.isSelected();
        for(int i = 0; i<obr.length; i++){
            
            ArrayList<Hladina> a = new ArrayList<Hladina>(1);
            ho[i] = new HladinaObrys(d, o, vDilky[iVzhled], vObrys[iVzhled], null/*new Color((float)Math.random(),(float)Math.random(),(float)Math.random() )*/, Hladina.CENTER, true,new Rectangle(-5,-5,-90,-90));
            a.add(ho[i]);
            obr[i] = new ObrazecShow(skl,Color.white,a);
            ho[i].prepniVzhled(sel ? Hladina.VZHLED_DILKY : Hladina.VZHLED_OBRYS);
            obr[i].addMouseListener(this);
            obr[i].addMouseWheelListener(this);
            panObr[i]= new JPanel(new BorderLayout());
            panObr[i].add(obr[i],BorderLayout.CENTER);
            lblObr[i] = new JLabel("Obrazec " + i);
            //lblObr[i].addMouseWheelListener(this);
            lblObr[i].setHorizontalAlignment(SwingConstants.CENTER);
            panObr[i].add(lblObr[i],BorderLayout.SOUTH);
            //obr[i].setVisible(false);
            //lblObr[i].setVisible(false);
            panObr[i].setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
            setSelected(i);
            panCenter.add(panObr[i]);
        }
        panCenter.setBackground(Color.white);
        panCenter.validate();
        getContentPane().add(panCenter,BorderLayout.CENTER);
        getContentPane().validate();
        //panCenter.addMouseWheelListener(this);
        obrInit = true;
        
        //frmEdit = new TangramEditFrame(this,database);
        //frmFilter = new FilterFrame(this,frmDescription);
        //frmMass = new MassDescriptionFrame(this,frmDescription);
        //frmMass.show();
    }
    
    public void mouseWheelMoved(MouseWheelEvent e) {
        //System.out.println("mouse wheel moved: "+this.getFocusOwner().getName());
        scrollNahledy.setValue(scrollNahledy.getValue()+e.getWheelRotation());
    }
  /*public int getFirstSelected(){
      int i;
      for (i = 0; i < database.getSize(); i++) {
          if (database.isSelected(i))
              break;
      }
      return i;
  }*/
    
    public void adjustmentValueChanged(AdjustmentEvent e){
        showNahledy(e.getValue()*wCount);
    }
    
    private void setSelected(){
        for(int j = 0; j<Count; j++)
            setSelected(j);
    }
    
    private void setSelected(int i){
        boolean sel = database.isSelected(i+pozice);
        panObr[i].setBackground( sel ? Color.lightGray : Color.white);
    }
    
    private void showEditFrame(int i, boolean solve){
        TangramEditFrame frm = new TangramEditFrame(this,database);
        frm.showObrazec(i, solve ? TangramEditFrame.MODE_EDIT : TangramEditFrame.MODE_SOLVE);
        windowOpened(frm);
    }
    
    private void showLayoutFrame(){
        LayoutFrame frm = new LayoutFrame(this, database, 297, 210);
        frm.showDocument(true);
        //windowOpened(frm);
    }
    
    public void mouseClicked(MouseEvent e){
        int i;
        for(i = 0; i < obr.length; i++){
            if(((Obrazec)e.getComponent())==obr[i])
                break;
        }
        if(e.getButton()==MouseEvent.BUTTON1){
            if(!e.isControlDown()){
                database.selectAll(false);
                database.setSelection(i+pozice,true);
            } else if(e.getClickCount() == 1)
                database.toggleSelection(i + pozice);
            if(e.getClickCount()>1)
                showEditFrame(i+pozice, e.isShiftDown());
        }
        else if(e.getButton()==MouseEvent.BUTTON2)
            showEditFrame(i+pozice, e.isControlDown());
        else if(e.getButton()==MouseEvent.BUTTON3)
            showDescription(i + pozice,null,false);
        return;
    }
    public void mouseEntered(MouseEvent e){}
    public void mouseExited(MouseEvent e){}
    public void mousePressed(MouseEvent e){}
    public void mouseReleased(MouseEvent e){}
    
    private String getTooltipText(Database.ObrazecData od){
        String s = new String(sTooltipFormatString);
        //Regex r = new Regex();
        s = s.replaceAll("%name", od.nazev);
        //s.replaceAll(new Regex("name",""), od.nazev);
        s = s.replaceAll("%description", od.popis);
        s = s.replaceAll("%author", od.autor);
        s = s.replaceAll("%date", df.format(od.datum));
        String kwds = new String();
        boolean first = true;
        for(String kwd: od.keywords){
            kwds += first ? kwd : ", " + kwd;
            first = false;
        }
        s = s.replaceAll("%keywords", kwds);
        return s;
    }
    private String getLabelText(Database.ObrazecData od){
        if(mniShowName.isSelected())
            return od.nazev;
        else if(mniShowAuthor.isSelected())
            return od.autor;
        else if(mniShowDate.isSelected())
            return dfLabel.format(od.datum);
        return null;
    }
    private void showNahled(int pos, int i){
        //pos = index prvniho viditelneho nahledu
        //i = index zobrazovaneho nahledu
        int j = i - pos;
        //System.out.println("ShowNahled_" + String.valueOf(j));
        boolean labels = mniShowLabels.isSelected(), tooltips = mniShowTooltips.isSelected();
        if(i < database.getSize()){
            boolean invalid = false;
            if(labels != lblObr[j].isVisible()){
                invalid = true;
                lblObr[j].setVisible(labels);
            }
            if (obr[j].isVisible() == false) {
                obr[j].setVisible(true);
                invalid = true;
            }
            if(invalid)
                panObr[j].validate(); //nutne, pokud byla v dobe skrytych nahledu zmenena velikost okna
            Database.ObrazecData od = database.getObrazecData(i);
            ho[j].update(od.d, od.o);
            //obr[j].repaint();
            int copyFrom = pos - lastPos + j;
            String sTip;
            if(!invalid && lastPos>-1 && copyFrom!=j && copyFrom >=0 && copyFrom<Count){
                obr[j].repaint(obr[copyFrom]);
                sTip = tooltips ? obr[copyFrom].getToolTipText() : null;
                obr[j].setToolTipText(sTip);
                lblObr[j].setToolTipText(sTip);  
                //System.out.println("Copying " + String.valueOf(copyFrom) + " to " + String.valueOf(j));
            } else{
                    obr[j].update();
                    sTip = tooltips ? sTip = getTooltipText(od) : null;
                    obr[j].setToolTipText(sTip);
                    lblObr[j].setToolTipText(sTip);
                //}
                //System.out.println("Painting " + String.valueOf(j));
            }
            //obr[j].paintImmediately(0,0,obr[j].getWidth(),obr[j].getHeight());
            lblObr[j].setText(getLabelText(od));
        } else if(obr[j].isVisible()==true){
            obr[j].setVisible(false);
            lblObr[j].setVisible(false);
        }
        setSelected(j);
    }
    
    /*private void refreshNahledy(int what){
        int lim = lastPos + Count;    
        for(i = lastPos; i< lim; i++)
            refreshNahled(i, what);
    }
    
    private void refreshNahled(int i, int what){
        int iObr = i - lastPos;
        if(iObr>=0 && iObr<=Count){
            if(i>= database.getSize()){
                
            }
            Database.ObrazecData od = database.getObrazecData(i);
            if((what & Database.CHANGED_OBRAZEC) != 0){
                ho[j].update(od.d, od.o);
                obr[j].update();
            }
            if((what & Database.CHANGED_OBRAZEC) != 0){
            //obr[j].repaint();
            int copyFrom = pos - lastPos + j;
            String sTip;
            if(lastPos>-1 && copyFrom!=j && copyFrom >=0 && copyFrom<Count){
                obr[j].repaint(obr[copyFrom]);
                sTip = obr[copyFrom].getToolTipText();
                obr[j].setToolTipText(sTip);
                lblObr[j].setToolTipText(sTip);  
                //System.out.println("Copying " + String.valueOf(copyFrom) + " to " + String.valueOf(j));
            } else{
                    sTip = getTooltipText(od);
                    obr[j].setToolTipText(sTip);
                    lblObr[j].setToolTipText(sTip);
                //}
                //System.out.println("Painting " + String.valueOf(j));
            }
            //obr[j].paintImmediately(0,0,obr[j].getWidth(),obr[j].getHeight());
            lblObr[j].setText(od.nazev);
            
        }
    }*/
    
    private void showNahledy(int pos){
        if(pos == lastPos)
            return;
        int i = 0;
        int lim = pos + Count;
        pozice = pos;
        if(lastPos < pos)
            for(i = pos; i< lim; i++)
                showNahled(pos, i);
        else
            for(i = lim - 1; i>=pos; i--)
                showNahled(pos, i);
        lastPos = pos;
        if(isActive())
            scrollNahledy.requestFocus();//fokus muze mit skryty obrazec, takze by nefungovalo kolecko
    }
    public void adjustScrollbar(int val, int newCount){
        //newCount == -1 => obrazce se zmenily
        //newCount == 0 => obrazce zustaly, zmenilo se jen posunuti
        //newCount > 0 => pribyly obrazce na konci
        
        if(newCount == -1)
            lastPos = -1;
        
        int size =(int) Math.ceil((double) database.getSize()/wCount);
        int oldVal = scrollNahledy.getValue();
        //System.out.println("oldVal=" + String.valueOf(oldVal));
       /*int pos = 0;
       if(val == SCROLL_KEEP)
           pos = lastPos;
       else if(val == SCROLL_LAST)
           pos = (size-hCount) * wCount;*/
        //lastPos = -1;
        int p = 0; //SCROLL_FIRST
        if(val == SCROLL_KEEP){
            int x = size <= hCount ? 0 : size - hCount;
            p = scrollNahledy.getValue() > x ? x : scrollNahledy.getValue();
        } else if(val == SCROLL_LAST)
            p = size <= hCount ? 0 : size - hCount;
        //System.out.println("p=" + String.valueOf(p));
        if(scrollNahledy.getMaximum() != size)
            scrollNahledy.setValues(p, hCount, 0, size);
        else if(oldVal != p)
            scrollNahledy.setValue(p);
        
        if(scrollNahledy.getValue() == oldVal && newCount > 0){
            int pos = scrollNahledy.getValue() * wCount;
            int lim = Math.min(database.getSize(),pos+Count);
            for(int i=database.getSize()-newCount; i<lim; i++)
                showNahled(pos, i);
            setSelected();
        } else{
            //if(newCount<=0)
            //    lastPos = -1;
            showNahledy(scrollNahledy.getValue() * wCount);
        }
    }
    
    public void updateNahledy(){
        int pos = lastPos;
        lastPos = -1;
        showNahledy(pos);
    }
    
    /*public void refreshNahledy(int what, i){
        for(int i = 0; i < Count; i++){
            ho[i].up
//if((what & Database.CHANGED_OBRAZEC) != 0)
        }
                    obr[j].update();
                //if((what & Database.CHANGED_DESCRIPTION) != 0){

    }*/
    public void setTitle(){
        String file = database.getFile();
        String s = (file == null ? "new" : file.substring(file.lastIndexOf(File.pathSeparatorChar) + 1)) + (database.hasChanged() ? sChanged : sUpToDate);
        Skladacka actSet = database.getActSet();
        boolean sel = database.isSelected();
        if(actSet == null){
            s = sEmptyString;
            setTitle(sApp);
        } else {
            s = actSet.nazev + sLFigureBracket + s + sRFigureBracket + sDash;
            s = database.isFiltered() ? s + "filtered " + database.filteredCount() + " of " + database.getSizeAll() + sFigures : s + database.getSizeAll() + sFigures;
            if(sel)
                s = s + " / " + String.valueOf(database.selectedCount()) + " selected";
            setTitle(sApp + sDash + s);
        }
        mniDelete.setEnabled(sel);
        mniCopy.setEnabled(sel);
        mniMassDescribe.setEnabled(sel);
        mniDescribe.setEnabled(sel);
        mniSolve.setEnabled(sel);
        mniEdit.setEnabled(sel);
        //mniSolver.setEnabled(pentomino || sel);
        mniPast.setEnabled(database.isPastable());
        mniSave.setEnabled(database.hasChanged());
        mniZrusFilter.setEnabled(database.isFiltered());
    }
    
    private static final int LOAD = 0, SAVE = 1, SAVEAS = 2, FILTER = 3, DESCRIBE = 4, REPAIR = 5;

    public void update(Graphics g) {
    }
    private class LoadSaveThread extends Thread{
        private int what;
        private Object p1, p2;
        public LoadSaveThread(int what, Object p1, Object p2){
            this.p1 = p1;
            this.p2 = p2;
            this.what = what;
        }
        public LoadSaveThread(int what, Object p1){
            this.p1 = p1;
            this.what = what;
        }
        public LoadSaveThread(int what){
            this.what = what;
        }
        @Override
        @SuppressWarnings("unchecked")
        public void run(){
            database.ignoreNextEvent();
            switch(what){
                case LOAD:
                    database.openFile((String) p1, frmProgress);
                    break;
                case SAVE:
                    database.save(frmProgress);
                    break;
                case SAVEAS:
                    database.saveAs((String) p1, frmProgress);
                    break;
                case FILTER:
                    database.filter((Database.ObrazecFilter)p1, frmProgress);
                    break;
                case DESCRIBE:
                    database.describeSelected((Database.ObrazecData)p1,(Vector<String>)p2,frmProgress);
                    break;
                case REPAIR:
                    database.repair(frmProgress);
                    break;
            }
        }
    };
    
    public void repair(){
        Thread openThread = new LoadSaveThread(REPAIR);
        openThread.start();
        frmProgress.init(this, "Repairing database, please wait...", "Normalizing and recalculationg figure");
        if (!frmProgress.done)
            frmProgress.setVisible(true);
        //setTitle();
        ignoreLastChange = true;
        database.changed(Database.CHANGED_NOTHING, -2, false);
        adjustScrollbar(SCROLL_KEEP, -1); //redraw all figures shown
        setTitle();
    }
    
    
    public void openDatabase(File f){
        if(f != null){
            Thread openThread = new LoadSaveThread(LOAD, f.getPath());
            openThread.start();
            frmProgress.init(this, "Loading database, please wait...", "Loading figure");
            if(!frmProgress.done)
                frmProgress.setVisible(true);
            if(!frmProgress.success)//if (!database.open(f, frmProgress))
                return;
        }
        Skladacka skl = database.getActSet();
        chooserOpen.setFileFilter(skl.getFileFilter());
        loadKeywords(skl.fKeywords);
        if(!obrInit)
            initObrazce();
        if(frmTangramSolver != null){
            frmTangramSolver.dispose();
            frmTangramSolver = null;//TODO kill thread
        }
        mniPentominos.setVisible(pentomino = skl.isPentomino());
        for (int i = 0; i < Count; i++)
            obr[i].setSkladacka(skl,null,null,null);
        chooserSave.resetChoosableFileFilters();
        chooserSave.addChoosableFileFilter(skl.getFileFilter());
        //lastPos = -1;
        ignoreLastChange = true;
        database.changed(Database.CHANGED_NOTHING, -2, false);
        adjustScrollbar(SCROLL_FIRST,-1);
        mnuSet.setEnabled(true);
        mnuSelection.setEnabled(true);
        mnuOrganize.setEnabled(true);
        mniRepair.setEnabled(true);
        setTitle();
        //mniShowSolutions.setSelected(false);
        //mniShowSolutions.setEnabled(true);
        //updateNahledy(false);
        //showNahledy(0);
    }
    
    public boolean save(){
        if(database.needsSaveAs())
            return false;
        Thread openThread = new LoadSaveThread(SAVE);
        openThread.start();
        frmProgress.init(this,"Saving database, please wait...", "Saving figure");
        if(!frmProgress.done)
            frmProgress.setVisible(true);
        database.changed(Database.CHANGED_NOTHING, -2, false);
        return frmProgress.success;
    }
    
    public void saveAs(){
        int ret = chooserSave.showSaveDialog(this);
        if (ret == JFileChooser.APPROVE_OPTION) {
            File f = chooserSave.getSelectedFile();
            Thread openThread = new LoadSaveThread(SAVEAS, ((Skladacka.DatabaseFilter)chooserSave.getFileFilter()).validate(f).getAbsolutePath());
            openThread.start();
            frmProgress.init(this,"Saving database, please wait...", "Saving figure");
            if(!frmProgress.done)
                frmProgress.setVisible(true);
            database.changed(Database.CHANGED_NOTHING, -2, false);
        }
    }
    
    public void filter(Database.ObrazecFilter of){
        Thread openThread = new LoadSaveThread(FILTER, of);
        openThread.start();
        frmProgress.init(this, "Filtering figures, please wait...", "Filtering figure");
        if(!frmProgress.done)
            frmProgress.setVisible(true);
        //if(of.filterType == of.FILTER_ADD) //zvazit
        //    adjustScrollbar(SCROLL_LAST);
        //else
        //System.out.println(database.getSize());
        ignoreLastChange = true;
        database.changed(Database.CHANGED_NOTHING, -2, false);
        adjustScrollbar(SCROLL_FIRST,-1);
        setTitle();
    }
    
    public void mass(Vector<String> rem, Database.ObrazecData od){
        Thread openThread = new LoadSaveThread(DESCRIBE, od, rem);
        openThread.start();
        frmProgress.init(this,"Describing figures, please wait...","Describing figure");
        if(!frmProgress.done)
            frmProgress.setVisible(true);
        if(frmProgress.success){;
            database.changed(Database.CHANGED_NOTHING, -2, false);
        }
    }
    
    public void zrusFilter(){
        database.zrusFilter();
        ignoreLastChange = true;
        adjustScrollbar(SCROLL_FIRST,-1);
        //setTitle();
        //updateNahledy(false);
    }
    
    public void actionPerformed(ActionEvent e){
        int actionID = Integer.parseInt(e.getActionCommand());
        int returnVal, index;
        if(actionID >= ID_SKLADACKA0 && actionID < ID_WINDOW0){
            if(askSaveCancel())
                return;
            quitSolver();
            database.create(database.getSetsSorted()[actionID-ID_SKLADACKA0]);
            openDatabase(null);
        } else if(actionID >= ID_WINDOW0){
            lstWindows.get(actionID-ID_WINDOW0).restoreState();
        }
        switch (actionID) {
        case ID_SHOWSOLUTIONS:
            if(obrInit){
                boolean sel = mniShowSolutions.isSelected();
                for (int i = 0; i < ho.length; i++) {
                    ho[i].prepniVzhled(sel ? Hladina.VZHLED_DILKY : Hladina.VZHLED_OBRYS);
                    obr[i].update();
                }
            }
            break;
        case ID_DESCRIBE:
            showDescription(database.getLastSelectedIndex(),null,false);
            break;
        case ID_TOGGLECOLORS:
            iVzhled = (iVzhled + 1)  % vObrys.length;
            for(HladinaObrys hoi : ho)
                hoi.setVzhled(vDilky[iVzhled], vObrys[iVzhled]);
            for(Obrazec obri : obr)
                obri.update();
            break;
        case ID_COPY:
            if(database.copySelection())
                setTitle();
            break;
        case ID_PAST:
            if((index = database.pastSelection())>0){
                ignoreLastChange = true;
                adjustScrollbar(SCROLL_LAST,index);
                setTitle();
            }
            break;
        case ID_INVERT:
            database.invertSelection();
            setSelected();
            //setTitle();
            break;
        case ID_SELNONE:
            database.selectAll(false);
            setSelected();
            //setTitle();
            break;
        case ID_SELALL:
            database.selectAll(true);
            setSelected();
            //setTitle();
            break;
        case ID_FILTER:
            index = database.getLastSelectedIndex();
            if(index == -1)
                index = 0;
            FilterFrame frmFilter = new FilterFrame(this,database);
            frmFilter.show(index, null);
            frmFilter.showDocument(true);
            break;
        case ID_ZRUSFILTER:
            zrusFilter();
            break;
        case ID_REPAIR:
            repair();
            break;
        case ID_KONEC:
            exit();
            break;
        case ID_OPEN:
            if(closeAllWindows())
                break;
            if(askSaveCancel())
                break;
            returnVal = chooserOpen.showOpenDialog(this);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                //update();
                quitSolver();
                openDatabase(chooserOpen.getSelectedFile());
            }
            break;
        case ID_SAVE:
            if(!save())
                saveAs();
            else
                setTitle();
            break;
        case ID_SAVEAS:
            saveAs();
            break;
        case ID_EDIT:
            showEditFrame(database.getLastSelectedIndex(),true);
            break;
        case ID_SOLVE:
            showEditFrame(database.getLastSelectedIndex(),false);
            break;
        case ID_NEWFIGURE:
            showEditFrame(-1, false);
            break;
        case ID_LAYOUTEDITOR:
            showLayoutFrame();
            break;
        case ID_DELETE:
            int ret = database.selectedCount();
            if(ret == 0)
                break;
            ret = JOptionPane.showConfirmDialog(this, "Deleting " + String.valueOf(ret) + " figure(s), are you sure?", "Confirm deletion", JOptionPane.OK_CANCEL_OPTION);
            if (ret == JOptionPane.OK_OPTION)
                if(database.deleteSelected())
                    adjustScrollbar(SCROLL_KEEP,-1);
                //resetShowed();
                //setTitle();
            break;
        case ID_MASSDESCRIBE:
            MassDescriptionFrame frmMass = new MassDescriptionFrame(this,database);
            frmMass.showDocument(true);
            break;
        case ID_SORTBYNAME:
            database.sort(database.cmpName, mniSortReverse.isSelected());
            break;
        case ID_SORTBYDATE:
            database.sort(database.cmpDate, mniSortReverse.isSelected());
            break;
        case ID_SORTBYAUTHOR:
            database.sort(database.cmpAuthor, mniSortReverse.isSelected());
            break;
        case ID_RUNPENTOMINOS:
            if(!pentomino)
                break;
            if (frmPentominos == null)
                frmPentominos = new PentominoSolverFrame(this, database, 8, 8);
            if(frmPentominos.isVisible())
                frmPentominos.toFront();
            else
                frmPentominos.showDocument(true);
            break;
        case ID_RUNSOLVER:
            Database.ObrazecData odSolve = database.getLastSelected();
            int iSolve = odSolve == null ? database.getSize()==0 ? -1 : 0 : database.getIndex(odSolve);
            if (frmTangramSolver == null)
                frmTangramSolver = new TangramSolverFrame(this, database);
            if(frmTangramSolver.isVisible())
                frmTangramSolver.toFront();
            else
                frmTangramSolver.showDocument(true);
            frmTangramSolver.show(iSolve, null);
            break;
        case ID_SHOWTOOLTIPS: case ID_SHOWLABELS:  case ID_SHOWNAME:  case ID_SHOWDATE:  case ID_SHOWAUTHOR:
            updateNahledy();
            break;
        case ID_CLOSEALL:
            closeAllWindows();
            break;
        case ID_CASCADE: {
            int x = 0, y = 0;
            Dimension d = getToolkit().getScreenSize();
            for(DocumentWindow doc: lstWindows){
                Window wnd = ((Window)doc);
                if(y > d.height - 100 || x > d.width - 50){
                    y = 0;
                    x = 0;
                }
                wnd.setLocation(x, y);
                x += 40;
                y += 40;
                wnd.toFront();
            }
        }
            break;
        case ID_HELPABOUT:
            JOptionPane.showConfirmDialog(this,"<html><h1>TangramBase 1.0</h1><p>August 2007</p><br><p>Jan Popelka</p><p>popelka@czech-ware.net</p>","About", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE);
            break;
        case ID_TILE:
            List<TangramEditFrame> lstBoard = new LinkedList<TangramEditFrame>();
            for(DocumentWindow wnd: lstWindows)
                if(wnd instanceof TangramEditFrame)
                    lstBoard.add((TangramEditFrame)wnd);
            int x = (int)Math.ceil(Math.sqrt(lstBoard.size()));
            Dimension d = getToolkit().getScreenSize();
            int w = d.width / x, h = d.height / x;
            ListIterator<TangramEditFrame> it = lstBoard.listIterator();
            xfor:for(int i=0; i<x; i++)
                for(int j=0; j<x; j++){
                    if(!it.hasNext())
                        break xfor;
                    TangramEditFrame wnd = it.next();
                    wnd.setSize(w, h);
                    wnd.setLocation(j*w, i*h);
                    wnd.toFront();
                }
            break;
        }
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                TangramFrame tframe = new TangramFrame();
                int w = 800, h = 600;
                tframe.setSize(w,h);
                try {
                    TangramEditFrame.wavSolved =  Applet.newAudioClip(new URL("file:" + (new File(TangramEditFrame.FILE_SOUND)).getAbsolutePath()));
                } catch(Exception e){}
                TangramFrame.centerOnScreen(tframe);
                //tframe.setExtendedState(Frame.MAXIMIZED_BOTH);
                //tframe.setTitle("TangramBase");
                tframe.setVisible(true);
            }
        });
    }
    public void quitSolver(){
        quittingPentominoSolver = frmPentominos == null ? false : frmPentominos.quit();
        quittingSolver = frmTangramSolver == null ? false : frmTangramSolver.quit();
    }
    
    public void pentominoSolutionFound(String nazev, int solCount,int rows, int cols, int solution[][]){
        if(quittingPentominoSolver)
            return;
        Skladacka skl = database.getActSet();
        Poloha p[] = PentominosConversion.convert(rows, cols, solution);
        Dilek d[] = skl.getDilky(p);
        Dilek.vycentruj(d);
        Obrys o = new Obrys(d);
        Database.ObrazecData od = new Database.ObrazecData(skl,d,o.od);
        od.autor = PentominoSolverFrame.solverName;
        if(!nazev.isEmpty())
            nazev+=" ";
        od.nazev = nazev + "Solution #" + String.valueOf(solCount);
        database.addObrazecData(od);
        adjustScrollbar(SCROLL_LAST,1);
        //setTitle();
    }
    
    public void solutionFound(String nazev, int solCount, Dilek d[]) {
        if(quittingSolver)
            return;
        Skladacka skl = database.getActSet();
        Dilek.vycentruj(d);
        Obrys o = new Obrys(d);
        Database.ObrazecData od = new Database.ObrazecData(skl, d, o.od);
        od.autor = TangramSolverFrame.solverName;
        if(!nazev.isEmpty())
            nazev+=" ";
        od.nazev = nazev + "Solution #" + String.valueOf(solCount);
        database.addObrazecData(od);
        adjustScrollbar(SCROLL_LAST, 1);
        //setTitle();
    }
    public void solverNotRunning(){
        quittingSolver = false;
        //System.out.println("solver quitted");
    }
    
    public void pentominoSolverNotRunning(){
        quittingPentominoSolver = false;
    }
    
    private DescriptionFrame showDescription(int i, Database.ObrazecData od, boolean modal){
        DescriptionFrame frm = new DescriptionFrame(this, database);
        frm.show(i, i < 0 ? od : database.getObrazecData(i));
        frm.setModalityType(modal ? Dialog.ModalityType.APPLICATION_MODAL : Dialog.ModalityType.MODELESS);
        frm.showDocument(true);
        return frm;
    }
    public int replaceObrazec(Database.ObrazecData odOld, Database.ObrazecData od, int i){            
        if(i < 0)
            return addObrazec(od, i);
        if(showDescription(-1, od, true).confirmed){
            odOld.shallowCopy(od);
            database.changed(Database.CHANGED_ALL, i, true);
            return i;
        }
        return -1;
    }

    public int addObrazec(Database.ObrazecData od, int i){
        int ret = -1;//TODO ???
        od = new Database.ObrazecData(od);
        
        if(showDescription(-1, od, true).confirmed){
            ret = database.addObrazecData(od);
            adjustScrollbar(SCROLL_LAST,1);
        }
        return ret;
    }

    private void updateWindowsMenu(){
        mnuWindows.removeAll();
        mnuWindows.add(mniCloseAll);
        mnuWindows.add(mniCascade);
        mnuWindows.add(mniTile);
        int i = 0;
        for(DocumentWindow frm: lstWindows){
            i++;
            JMenuItem mniWindow = new JMenuItem();
            frm.setMenuItem(i,mniWindow);
            mniWindow.setActionCommand(String.valueOf(ID_WINDOW0+i-1));
            mniWindow.addActionListener(this);
            if(i==1)
                mnuWindows.addSeparator();
            mnuWindows.add(mniWindow);
        }
        mnuWindows.setEnabled(i > 0);
    }
    public boolean windowOpened(DocumentWindow frm){
        if(frm instanceof Database.DatabaseChangeListener)
            database.addDatabaseChangeListener((Database.DatabaseChangeListener)frm);
        boolean ret = lstWindows.add(frm);
        updateWindowsMenu();
        return ret;
    }
    public boolean windowClosed(DocumentWindow frm){
        if(frm instanceof Database.DatabaseChangeListener)
            database.removeDatabaseChangeListener((Database.DatabaseChangeListener)frm);
        boolean ret = lstWindows.remove(frm);
        updateWindowsMenu();
        return ret;
    }
    
    public static void centerOnScreen(Window frm){
        Dimension d = frm.getToolkit().getScreenSize();
        frm.setLocation((d.width-frm.getWidth())/2,(d.height-frm.getHeight())/2);
    }
    
    public static void centerOnForm(Window parent, Window frm){
        Point p = parent.getLocationOnScreen();
        frm.setLocation(p.x + (parent.getWidth()-frm.getWidth())/2, p.y + (parent.getHeight()-frm.getHeight())/2);
    }
    
    private boolean ignoreLastChange = false;
    public boolean databaseChanged(int what, int i){
        if(ignoreLastChange){
            ignoreLastChange = false;
            return true;
        }
        setTitle();
        if(obrInit){
            if(what == Database.CHANGED_SELECTION){
                int iObr = i -lastPos;
                if(i > -1 && iObr>=0 && iObr<Count)
                    setSelected(iObr);
                else
                    setSelected();
            }
            else
                updateNahledy();//TODO optimalizovat bez prekreslovani vseho
        }
        return true;
    }
    
    private void setMenuItemText(){
        if(mniThis != null)
            mniThis.setText("(" + iWindow + ")  " + getTitle());
    }
    
    public void setMenuItem(int i, JMenuItem mni){
        iWindow = i;
        mniThis = mni;
        setMenuItemText();
    }
    
    public void windowStateChanged(WindowEvent ev){
        if((ev.getNewState() & JFrame.ICONIFIED) != 0)
            lastState = ev.getOldState();
    }
    
    public void restoreState(){
        setExtendedState(lastState);
        toFront();
    }
    
    public boolean showDocument(boolean jak){
        //boolean ret = jak ? frmDatabase.windowOpened(this):frmDatabase.windowClosed(this);
        setVisible(jak);
        if(jak)
            toFront();
        return jak;
    }
    public boolean hasChanged(){
        return database.hasChanged();
    }
    public boolean closeAllWindows(){
        boolean changed = false;
        for(DocumentWindow wnd: lstWindows){
            if(wnd.hasChanged()){
                changed = true;
                break;
            }
        }
        boolean leaveChanged = false;
        if(changed)
            leaveChanged = JOptionPane.showConfirmDialog(this,"Some windows contain unsaved changes, do you want to leave them open?","Unsaved changes",JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
        List<DocumentWindow> lstHide = new LinkedList<DocumentWindow>();
        for(DocumentWindow wnd: lstWindows)
            if(!wnd.hasChanged() || !leaveChanged)
                lstHide.add(wnd);
        for(DocumentWindow wnd: lstHide)
            wnd.showDocument(false);
        return leaveChanged;
    }
}
